package network;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.SSLSocket;

import log.Log;
import database.Doctor;
import database.Individual;
import database.Nurse;
import database.Patient;
import database.Record;
import database.RecordList;

public class ServerConnection extends Thread {

	private Server server;
	private SSLSocket socket;
	private ObjectOutputStream clientOut;
	private ObjectInputStream clientIn;
	private Individual user;
	private Pattern pattern = Pattern.compile("([^\"]\\S*|\".+?\")\\s*");

	public ServerConnection(Server server, SSLSocket socket, Individual user) {
		this.server = server;
		this.socket = socket;
		this.user = user;
		try {
			clientOut = new ObjectOutputStream(socket.getOutputStream());
			clientIn = new ObjectInputStream(socket.getInputStream());
		} catch (IOException e) {
			Log.log(Log.LOG_EXCEPTION, e.getMessage());
		}
		start();
	}

	/** 
	 * help
	 * whoami
	 * users
	 * read <patient> <record id>
	 * write <patient> <record id> <data>
	 * create <patient> <doctor> <nurse> <data>
	 * delete <patient> <record id>
	 */
	public void run() {
		while (socket.isConnected()) {
			Object obj;
			try {
				while ((obj = clientIn.readObject()) != null) {
					Log.log(Log.LOG_INFO, user.getName()
							+ " entered the command: " + obj);
					if (obj instanceof String) {
						String cmd = (String) obj;

						List<String> arg = new ArrayList<String>();
						Matcher m = pattern.matcher(cmd);
						while (m.find())
							arg.add(m.group(1).replace("\"", ""));

						if (arg.get(0).equals("help")) {
							clientOut
									.writeObject(" - whoami\n - read\n - write\n - create\n - delete\n - exit");
							clientOut.flush();
						} else if (arg.get(0).equals("whoami")) {
							if (user.read >= Individual.PERSONAL) {
								clientOut.writeObject(user);
								clientOut.flush();
							} else {
								clientOut.writeObject("Access denied.");
								clientOut.flush();
							}
						} else if (arg.get(0).equals("read")) {
							if (arg.size() == 1 || arg.get(1).equals("-?")) {
								clientOut.writeObject("read <patient>");
								clientOut.flush();
								break;
							}
							if (arg.size() < 2) {
								clientOut
										.writeObject("Too few arguments given.");
								clientOut.flush();
								break;
							}
							Individual patient = server.getStorage()
									.getIndividual(arg.get(1));
							if (patient instanceof Patient
									&& user.hasRights((Patient) patient,
											Individual.READ)) {
								RecordList rl = server.getStorage()
										.getIndividual(arg.get(1)).getRecords();
								Collections.sort(rl);
								clientOut.writeObject(rl);
								clientOut.flush();
							} else {
								clientOut.writeObject("Access denied.");
								clientOut.flush();
							}
						} else if (arg.get(0).equals("write")) {
							if (arg.size() == 1 || arg.get(1).equals("-?")) {
								clientOut
										.writeObject("write <patient> <record id> <data>");
								clientOut.flush();
								break;
							}
							if (arg.size() < 3) {
								clientOut
										.writeObject("Too few arguments given.");
								clientOut.flush();
								break;
							}
							Individual patient = server.getStorage()
									.getIndividual(arg.get(1));
							if (patient instanceof Patient
									&& user.hasRights((Patient) patient,
											Individual.WRITE)) {
								Record record = ((Patient) patient)
										.getRecords().getByID(
												Integer.parseInt(arg.get(2)));
								if (record != null) {
									record.setData(arg.get(3));
									clientOut.writeObject("Record written.");
									clientOut.flush();
								} else {
									clientOut.writeObject("Access denied.");
									clientOut.flush();
								}
							} else {
								clientOut.writeObject("Access denied.");
								clientOut.flush();
							}
						} else if (arg.get(0).equals("create")) {
							if (arg.size() == 1 || arg.get(1).equals("-?")) {
								clientOut
										.writeObject("create <patient> <nurse> <data>");
								clientOut.flush();
								break;
							}
							if (arg.size() < 4) {
								clientOut
										.writeObject("Too few arguments given.");
								clientOut.flush();
								break;
							}
							Individual ind = server.getStorage().getIndividual(
									arg.get(1));
							if (ind instanceof Patient
									&& user instanceof Doctor
									&& user.hasRights((Patient) ind,
											Individual.CREATE)) {
								Patient patient = (Patient) ind;
								Record record = new Record(server.getStorage()
										.getNextRecordID(), patient,
										(Doctor) user, (Nurse) server
												.getStorage().getIndividual(
														arg.get(2)), arg.get(3));
								((Nurse) server.getStorage().getIndividual(
										arg.get(2))).getPatients().add(patient);
								patient.addRecord(record);
								clientOut.writeObject("Record created:");
								clientOut.writeObject(record);
								clientOut.flush();
							} else {
								clientOut.writeObject("Access denied.");
								clientOut.flush();
							}
						} else if (arg.get(0).equals("delete")) {
							if (arg.size() == 1 || arg.get(1).equals("-?")) {
								clientOut
										.writeObject("delete <patient> <record id>");
								clientOut.flush();
								break;
							}
							if (arg.size() < 3) {
								clientOut
										.writeObject("Too few arguments given.");
								clientOut.flush();
								break;
							}
							Individual ind = server.getStorage().getIndividual(
									arg.get(1));
							if (ind instanceof Patient
									&& user.hasRights((Patient) ind,
											Individual.DELETE)) {
								((Patient) ind).getRecords().removeByID(
										Integer.parseInt(arg.get(2)));
								clientOut.writeObject("Record deleted.");
								clientOut.flush();
							} else {
								clientOut.writeObject("Access denied.");
								clientOut.flush();
							}
						} else {
							clientOut.writeObject("'" + cmd
									+ "' is not a valid command.");
							clientOut.flush();
						}
					}
				}
			} catch (EOFException e) {
				break;
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
}
